import java.awt.Point;
import java.awt.event.KeyEvent;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.JFileChooser;

public class FKeyEventProcessor extends DrawObjectEventProcessor
{
    // NOTE: ACTUALLY NO DRAW OBJECT, BUT KEYBOARD EVENTS PROCESSED IN THE SAME WAY!

    private final String SimulationTitleBarInfo1 = "Manual Activation (Space)";
    private final String SimulationTitleBarInfo2 = "Simulation Running";
    private final String SaveEmptyFileErrorMessage = "Error saving file: you cannot override the empty file - just load it (F1) to begin a new project from scratch and then save it (F3) with a different name.";

    private IOProgram IOProgram;
    private LoadSaveManager LoadSaveManager;
    private ObjectStorage ObjectStorage;
    private Settings Settings;

    private boolean ReducedDrawModeEnabled = false;

    private ArrayList<DrawObject> CopyAndPasteStartDrawObjects = new ArrayList<DrawObject>();

    public FKeyEventProcessor(IOProgram IOProgram, LoadSaveManager LoadSaveManager, ObjectStorage ObjectStorage, Settings Settings)
    {
        this.IOProgram = IOProgram;
        this.LoadSaveManager = LoadSaveManager;
        this.ObjectStorage = ObjectStorage;
        this.Settings = Settings;
    }

    private ArrayList<DrawObject> GetCopyAndPasteEndDrawObjects(ArrayList<DrawObject> DrawObjects)
    {
        ArrayList<DrawObject> CopyAndPasteEndDrawObjects = new ArrayList<DrawObject>();

        for (Iterator<DrawObject> i = DrawObjects.iterator(); i.hasNext();)
        {
            DrawObject o = i.next();
            if (o instanceof JavaCADUInputNode ||
                o instanceof JavaCADULearningNode ||
                o instanceof Node ||
                o instanceof SampleAndHoldInputNode ||
                o instanceof TextField ||
                o instanceof VisualFieldPixel)
            {
                CopyAndPasteEndDrawObjects.add(o);
            }
        }

        return CopyAndPasteEndDrawObjects;
    }

    private ArrayList<DrawObject> GetCopyAndPasteStartDrawObjects(ArrayList<DrawObject> DrawObjects)
    {
        ArrayList<DrawObject> CopyAndPasteStartDrawObjects = new ArrayList<DrawObject>();

        for (Iterator<DrawObject> i = DrawObjects.iterator(); i.hasNext();)
        {
            DrawObject o = i.next();
            if (o instanceof JavaCADUOutputNode ||
                o instanceof NeuronOutputNode ||
                o instanceof Node ||
                o instanceof SampleAndHoldOutputNode ||
                o instanceof TextField ||
                o instanceof VisualFieldPixel)
            {
                CopyAndPasteStartDrawObjects.add(o);
            }
        }

        return CopyAndPasteStartDrawObjects;
    }

    private LoadSaveDataEx LoadFile(String LoadPath, LoadSaveManager LoadSaveManager, boolean LoadNewOrInsert)
    {
        LoadSaveDataEx LoadSaveDataEx;

        if (LoadNewOrInsert)
        {
            LoadSaveDataEx = ObjectStorage.LoadAllFromFile(LoadPath);
        }
        else
        {
            int DrawObjectCountOld = ObjectStorage.GetDrawObjectCount();

            LoadSaveDataEx = ObjectStorage.LoadAdditionalFromFile(LoadPath);

            ObjectStorage.UnSetPartOfMultiSelectionForAllObjects();

            int DrawObjectCountNew = ObjectStorage.GetDrawObjectCount();

            for (int i = DrawObjectCountOld; i < DrawObjectCountNew; i++)
                ObjectStorage.SetPartOfMultiSelection(i);
        }

        return LoadSaveDataEx;
    }

    @SuppressWarnings("static-access")
    private String PromptLoadFile(MainWindow.GUIElements GUIElements, LoadSaveManager LoadSaveManager, boolean LoadNewOrInsert)
    {
        String DialogTitle = "";

        if (LoadNewOrInsert)
        {
            DialogTitle = "Load file";
        }
        else
        {
            DialogTitle = "Insert file content";
        }

        JFileChooser Dialog = new JFileChooser(); // https://www.codejava.net/java-se/swing/show-save-file-dialog-using-jfilechooser

        Dialog.setDialogTitle(DialogTitle);
        Dialog.setCurrentDirectory(new File(Settings.GetWorkingDirectory()));

        int DialogResult = Dialog.showOpenDialog(GUIElements.GetMainWindow());

        if (DialogResult == JFileChooser.APPROVE_OPTION)
        {
            return Dialog.getSelectedFile().getAbsolutePath();
        }
        else
        {
            return "";
        }
    }

    @SuppressWarnings("static-access")
    private String PromptSaveFile(MainWindow.GUIElements GUIElements, LoadSaveManager LoadSaveManager, boolean SaveNewOrOverride)
    {
        if (SaveNewOrOverride)
        {
            String DialogTitle = "Save as .ncide file";

            JFileChooser Dialog = new JFileChooser();

            Dialog.setDialogTitle(DialogTitle);
            Dialog.setCurrentDirectory(new File(Settings.GetWorkingDirectory())); // TEST, make this dynamic

            int DialogResult = Dialog.showSaveDialog(GUIElements.GetMainWindow()); // https://www.codejava.net/java-se/swing/show-save-file-dialog-using-jfilechooser

            if (DialogResult == JFileChooser.APPROVE_OPTION)
            {
                String SavePath = Dialog.getSelectedFile().getAbsolutePath();
                String SavePathSuffix = ".ncide";

                // add file name suffix if not there
                if (SavePath.length() < SavePathSuffix.length() || NOT(SavePath.substring(SavePath.length() - SavePathSuffix.length()).toLowerCase().equals(SavePathSuffix.toLowerCase()))) // not already ".ncide" at end of file name?
                {
                    SavePath += SavePathSuffix;
                }

                String WorkingDirectoryNew = "";

                int LastSlashPos = SavePath.lastIndexOf("/");
                if (LastSlashPos < 0)
                    LastSlashPos = SavePath.lastIndexOf("\\");
                if (LastSlashPos < 0)
                    LastSlashPos = 0;

                WorkingDirectoryNew = SavePath.substring(0, LastSlashPos);

                // working directory is just the last directory
                // where the user manually saved a file

                Settings.SetWorkingDirectory(WorkingDirectoryNew);
                Settings.SaveSettingsFile();

                VerifyEmptyFileExists(Settings.GetWorkingDirectory());

                return SavePath;
            }
            else
            {
                return "";
            }
        }
        else
        {
            return ""; // this function is not suitable for this file saving mode
        }
    }

    private LoadSaveDataEx SaveFile(MainWindow MainWindow, String SavePath, LoadSaveDataEx LoadSaveDataEx, LoadSaveManager LoadSaveManager, boolean SaveOrOverride)
    {
        // create auto-backup
        try
        {
            String BackUpPath = "";

            Path CopySourcePath = Paths.get(SavePath);
            Path CopyTargetPath = null;

            for (int m = 1; m < 1000; m++)
            {
                int SavePathLastSlashPos = SavePath.lastIndexOf("/");
                if (SavePathLastSlashPos < 0)
                    SavePathLastSlashPos = SavePath.lastIndexOf("\\");
                if (SavePathLastSlashPos < 0)
                    SavePathLastSlashPos = -1;

                String BackUpDir = SavePath.substring(0, SavePathLastSlashPos + 1) + "BackUp/";

                if (!((new File(BackUpDir)).exists()))
                    (new File(BackUpDir)).mkdirs();

                BackUpPath = BackUpDir + "{backup" + String.format("%04d", m) + "." + SavePath.substring(SavePathLastSlashPos + 1) + "}";

                System.out.println(BackUpPath);

                CopyTargetPath = Paths.get(BackUpPath);

                if (NOT(Files.exists(CopyTargetPath)))
                    break;
                else
                    CopyTargetPath = null;
            }
            if (CopySourcePath != null && CopyTargetPath != null)
            {
                Files.copy(CopySourcePath, CopyTargetPath, StandardCopyOption.REPLACE_EXISTING);
            }
        }
        catch (Exception e)
        {
            System.out.println("error creating backup of file to override: " + e.getMessage()); // just go on (for now)
        }

        // do actual saving
        LoadSaveDataEx LoadSaveDataExReturned = ObjectStorage.SaveAllToFile(SavePath, LoadSaveDataEx);

        return LoadSaveDataExReturned;
    }

    public void ScrollAfterZoomFactorChange(MainWindow.GUIElements GUIElements, MouseEventProcessor MouseEventProcessor, Point MousePosUnZoomedBeforeZoomFactorChange)
    {
        Point MousePosUnZoomedAfter = DrawObject.AlignOnGrid(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow())));

        Point MoveAmount = new Point(MousePosUnZoomedAfter.x - MousePosUnZoomedBeforeZoomFactorChange.x, MousePosUnZoomedAfter.y - MousePosUnZoomedBeforeZoomFactorChange.y); // new pos mouse is pointing to (after zooming) shall lie at old one (before zooming)

        ObjectStorage.MoveAll(MoveAmount);

        ShowZoomFactorChangeMessage(GUIElements);
    }

    private void ShowZoomFactorChangeMessage(MainWindow.GUIElements GUIElements)
    {
        GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoTextContaining("Zoom ");
        GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText(ZoomManager.GetZoomInfoText(ZoomManager.GetZoomFactor()), true, false, false, false);
    }

    private void StartSimulation(MainWindow.GUIElements GUIElements, boolean ManualActivationOnly)
    {
        GUIElements.GetMainWindow().EnableHandsomeDrawOrder(); // draw VisualFields and VisualFieldsPixels at last to not be hidden by Lines

        GUIElements.GetIOPanel().GetIOTextArea().setEditable(false);

        IOProgram.StartExecution(GUIElements, ObjectStorage, ManualActivationOnly);

        if (ManualActivationOnly)
            GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText(SimulationTitleBarInfo1, false, false, true, false);
        else
            GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText(SimulationTitleBarInfo2, false, false, true, false);
    }

    private void StopSimulation(MainWindow.GUIElements GUIElements)
    {
        GUIElements.GetMainWindow().DisableHandsomeDrawOrder(); // draw again in exactly that order the DrawObjects were created/added

        IOProgram.StopExecution();

        // NOTE: there was a problem with vanished keyboard cursor,
        // as a counter measure, try all of the following:

        GUIElements.GetIOPanel().GetIOTextArea().setEnabled(true);
        GUIElements.GetIOPanel().GetIOTextArea().setVisible(true);
        GUIElements.GetIOPanel().GetIOTextArea().setEditable(true);
        GUIElements.GetIOPanel().GetIOTextArea().getCaret().setVisible(true); // https://groups.google.com/g/comp.lang.java.programmer/c/VcjWydFYR8E

        GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText(SimulationTitleBarInfo1);
        GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText(SimulationTitleBarInfo2);
    }

    @SuppressWarnings("static-access")
    @Override
    public void TryProcessUserKeyboardAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor, ObjectStorage ObjectStorage, IOProgram IOProgram)
    {
        if (!(KeyboardEventsToBeProcessed(GUIElements)))
            return;

        if (KeyEventProcessor.AnyKeyPressed())
        {
            // ****************************************************************
            // b (restore BackUp (actually no F-Key, but fits in here))
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_B) &&
                KeyEventProcessor.IsControlPressed())
            {
                GUIElements.GetMainWindow().EnableBackUpRestore(
                    Settings.GetWorkingDirectory(),
                    LoadSaveManager.GetOverrideFileName()
                );

                GUIElements.GetMainWindow().DoRedraw();
            }
            // ****************************************************************
            // F1 (load new file)
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F1))
            {
                String LoadPath = PromptLoadFile(GUIElements, LoadSaveManager, true);

                if (LoadPath.length() > 0)
                {
                    StopSimulation(GUIElements); // doesn't continue right if loading a new file, don't know exactly why, but continuing simulation is 1) confusing for the user and 2) doesn't really make sense

                    GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Loading file", false, true, true, true);

                    LoadSaveDataEx LoadSaveDataEx = LoadFile(LoadPath, LoadSaveManager, true);

                    if (LoadSaveDataEx.GetSuccessOrError())
                    {
                        LoadSaveManager.ReactOnFileLoaded(LoadPath, false);

                        GUIElements.GetIOPanel().SetIOProgramString(LoadSaveDataEx.GetIOProgramString());
                        IOProgram.SetIOProgramString(LoadSaveDataEx.GetIOProgramString());

                        GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("LOADED", true, false, false, false);

                        ZoomManager.SetZoomFactor(LoadSaveDataEx.GetZoomFactor());
                        ShowZoomFactorChangeMessage(GUIElements);
                    }
                    else
                    {
                        LoadSaveManager.ReactOnFileLoadError();

                        Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error loading file: " + LoadSaveDataEx.GetErrorMessage());
                    }

                    GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Loading file");
                }

                GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
            }
            // ****************************************************************
            // F2 (load file content and insert)
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F2))
            {
                String LoadPath = PromptLoadFile(GUIElements, LoadSaveManager, false);

                if (LoadPath.length() > 0)
                {
                    GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Loading file", false, true, true, true);

                    LoadSaveDataEx LoadSaveDataEx = LoadFile(LoadPath, LoadSaveManager, false);

                    if (LoadSaveDataEx.GetSuccessOrError())
                    {
                        LoadSaveManager.ReactOnFileLoaded(LoadPath, true);

                        GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("INSERTED", true, false, false, false);
                    }
                    else
                    {
                        Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error loading file: " + LoadSaveDataEx.GetErrorMessage());
                    }

                    GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Loading file");
                }

                GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
            }
            // ****************************************************************
            // F3 (save to file)
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F3))
            {
                String SavePath = PromptSaveFile(GUIElements, LoadSaveManager, true);

                if (SavePath.length() > 0)
                {
                    if (SavePath.toLowerCase().endsWith("empty.ncide"))
                    {
                        Tools.ShowMessageDialog(GUIElements.GetMainWindow(), SaveEmptyFileErrorMessage);
                    }
                    else
                    {
                        GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Saving file", false, true, true, true);

                        LoadSaveDataEx LoadSaveDataEx = SaveFile(GUIElements.GetMainWindow(), SavePath, new LoadSaveDataEx(true, "", GUIElements.GetIOPanel().GetIOProgramString(), ZoomManager.GetZoomFactor()), LoadSaveManager, true);

                        if (LoadSaveDataEx.GetSuccessOrError())
                        {
                            LoadSaveManager.ReactOnFileSaved(SavePath, false);

                            GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("SAVED", true, false, false, false);
                        }
                        else
                        {
                            Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error saving file: " + LoadSaveDataEx.GetErrorMessage());
                        }

                        GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Saving file");
                    }
                }

                GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
            }
            // ****************************************************************
            // F4 (override last loaded or saved file)
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F4))
            {
                String SavePath = LoadSaveManager.GetOverridePath();

                if (SavePath != null && SavePath.length() > 0)
                {
                    if (SavePath.toLowerCase().endsWith("empty.ncide"))
                    {
                        Tools.ShowMessageDialog(GUIElements.GetMainWindow(), SaveEmptyFileErrorMessage);
                    }
                    else
                    {
                        GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Saving file", false, true, true, true);

                        LoadSaveDataEx LoadSaveDataEx = SaveFile(GUIElements.GetMainWindow(), SavePath, new LoadSaveDataEx(true, "", GUIElements.GetIOPanel().GetIOProgramString(), ZoomManager.GetZoomFactor()), LoadSaveManager, false);

                        if (LoadSaveDataEx.GetSuccessOrError())
                        {
                            LoadSaveManager.ReactOnFileSaved(SavePath, true);

                            GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("SAVED", true, false, false, false);
                        }
                        else
                        {
                            Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error overriding file: " + LoadSaveDataEx.GetErrorMessage());
                        }

                        GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Saving file");
                    }
                }
                else
                {
                    Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error overriding file: No (valid) file to override loaded or saved before");
                }

                GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
            }
            // ****************************************************************
            // F7 (copy selection or nodes to clipboard (actually a temp file resp. ArrayList))
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F7) &&
                NOT(KeyEventProcessor.IsControlPressed()) &&
                NOT(KeyEventProcessor.IsShiftPressed()))
            {
                GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Copying", false, true, true, true);

                LoadSaveDataEx LoadSaveDataEx = ObjectStorage.SaveMultiSelectionToFile("./NCIDE_clipboard.dat", new LoadSaveDataEx(true, "", GUIElements.GetIOPanel().GetIOProgramString(), ZoomManager.GetZoomFactor()));

                GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Copying");

                if (LoadSaveDataEx.GetSuccessOrError())
                {
                    GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("COPIED", true, false, false, false);
                }
                else
                {
                    Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error copying: " + LoadSaveDataEx.GetErrorMessage());
                }

                GUIElements.GetMainWindow().DoRedraw();
            }
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F7) &&
                (KeyEventProcessor.IsControlPressed() || KeyEventProcessor.IsShiftPressed()))
            {
                GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Copying", false, true, true, true);

                ArrayList<DrawObject> MultiSelectionObjects = ObjectStorage.GetMultiSelectionObjects();

                CopyAndPasteStartDrawObjects = GetCopyAndPasteStartDrawObjects(MultiSelectionObjects);

                ObjectStorage.UnSetPartOfMultiSelectionForAllObjects();

                GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Copying");

                GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("COPIED " + CopyAndPasteStartDrawObjects.size() + " CONNECTION OBJECTS", true, false, false, false);

                GUIElements.GetMainWindow().DoRedraw();
            }
            // ****************************************************************
            // F8 (paste content from clipboard resp. ArrayList)
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F8) &&
                NOT(KeyEventProcessor.IsControlPressed()) &&
                NOT(KeyEventProcessor.IsShiftPressed()))
            {
                GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Pasting", false, true, true, true);

                LoadSaveDataEx LoadSaveDataEx = LoadFile("./NCIDE_clipboard.dat", LoadSaveManager, false);

                GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Pasting");

                if (LoadSaveDataEx.GetSuccessOrError())
                {
                    GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("PASTED", true, false, false, false);
                }
                else
                {
                    Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error pasting: " + LoadSaveDataEx.GetErrorMessage());
                }

                GUIElements.GetMainWindow().DoRedraw();
            }
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F8) &&
                (KeyEventProcessor.IsControlPressed() || KeyEventProcessor.IsShiftPressed()))
            {
                GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Pasting", false, true, true, true);

                ArrayList<DrawObject> MultiSelectionObjects = ObjectStorage.GetMultiSelectionObjects();

                ArrayList<DrawObject> CopyAndPasteEndDrawObjects = GetCopyAndPasteEndDrawObjects(MultiSelectionObjects);

                boolean NeuronDrawObjectsOnly = true;

                for (int m = 0; m < MultiSelectionObjects.size(); m++)
                {
                    if (!(MultiSelectionObjects.get(m) instanceof Neuron) &&
                        !(MultiSelectionObjects.get(m) instanceof NeuronInputField) &&
                        !(MultiSelectionObjects.get(m) instanceof NeuronInputNode) &&
                        !(MultiSelectionObjects.get(m) instanceof NeuronOutputNode))
                    {
                        NeuronDrawObjectsOnly = false;
                        break;
                    }
                }

                int LineCreatedCount = 0;

                if (MultiSelectionObjects.size() >= 1 &&
                    NeuronDrawObjectsOnly)
                {
                    for (int n = 0; n < MultiSelectionObjects.size(); n++)
                    {
                        if (MultiSelectionObjects.get(n) instanceof Neuron)
                        {
                            CopyAndPasteEndDrawObjects.clear();

                            int InputCountRequired = CopyAndPasteStartDrawObjects.size();

                            for (int m = 0; m < InputCountRequired; m++)
                            {
                                Neuron.NeuronInputDrawObjects InputDrawObjects = ((Neuron) MultiSelectionObjects.get(n)).AddInputFieldAndNode();

                                ObjectStorage.AddObjectDirectly(InputDrawObjects.GetField());
                                ObjectStorage.AddObjectDirectly(InputDrawObjects.GetNode());

                                CopyAndPasteEndDrawObjects.add(InputDrawObjects.GetNode());
                            }

                            for (int i = 0; i < CopyAndPasteStartDrawObjects.size(); i++)
                            {
                                Line Line = new Line(CopyAndPasteStartDrawObjects.get(i), CopyAndPasteEndDrawObjects.get(i));

                                ObjectStorage.AddObjectDirectly(Line);

                                LineCreatedCount++;
                            }
                        }
                    }
                }
                else if (CopyAndPasteStartDrawObjects.size() == CopyAndPasteEndDrawObjects.size())
                {
                    for (int i = 0; i < CopyAndPasteStartDrawObjects.size(); i++)
                    {
                        Line Line = new Line(CopyAndPasteStartDrawObjects.get(i), CopyAndPasteEndDrawObjects.get(i));

                        ObjectStorage.AddObjectDirectly(Line);

                        LineCreatedCount++;
                    }
                }

                ObjectStorage.UnSetPartOfMultiSelectionForAllObjects();

                GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Pasting");

                GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("PASTED " + LineCreatedCount + " LINES", true, false, false, false);

                GUIElements.GetMainWindow().DoRedraw();
            }
            // ****************************************************************
            // F9 (help)
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F9))
            {
                GUIElements.GetMainWindow().ToggleHelpText();
                GUIElements.GetMainWindow().DoRedraw();
            }
            // ****************************************************************
            // F10 (show/hide IOPanel)
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F10))
            {
                boolean IPanelVisible = GUIElements.GetIOPanel().isVisible();

                IPanelVisible = !IPanelVisible;

                GUIElements.GetIOPanel().setVisible(IPanelVisible);

                if (IPanelVisible)
                {
                    GUIElements.GetIOPanel().requestFocus();

                    GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("F11 or F12", true, true, false, false);
                }
                else
                    GUIElements.GetMainWindow().requestFocus(); // important, or KeyListener _somehow_ "stays" in hidden TextArea

                GUIElements.GetMainWindow().DoRedraw(); // necessary (tested), or odd window salad
            }
            // ****************************************************************
            // F11 (start simulation, wait for manual activation (space bar) only)
            // F12 (start simulation, work up IOTextArea text and allow manual activation (space bar))
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F11) ||
                KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F12))
            {
                if (NOT(IOProgram.IsExecuted()))
                {
                    if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F11))
                    {
                        StartSimulation(GUIElements, true);
                    }
                    if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F12))
                    {
                        StartSimulation(GUIElements, false);
                    }
                }
                else
                {
                    StopSimulation(GUIElements);
                }

                GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
            }
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_R) &&
                KeyEventProcessor.IsControlPressed())
            {
                ReducedDrawModeEnabled = !ReducedDrawModeEnabled;

                if (ReducedDrawModeEnabled)
                    GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Drawing less to speed up", true, false, false, false);
                else
                    GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Drawing all", true, false, false, false);

                DrawObject.SetReducedDrawOrderEnabled(ReducedDrawModeEnabled);

                GUIElements.GetMainWindow().DoRedraw();
            }

            // ****************************************************************
            // F5 (toggle two default zoom settings)
            // F6 (restore previous custom zoom)
            if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F5) ||
                KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F6))
            {
                Point MousePosUnZoomedBeforeZoomFactorChange = DrawObject.AlignOnGrid(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow())));

                if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F5))
                {
                    if (!(ZoomManager.IsZoomFactorFixedSmall(ZoomManager.GetZoomFactor())))
                    {
                        ZoomManager.SetZoomFactorFixedSmall();
                    }
                    else if (!(ZoomManager.IsZoomFactorFixedLarge(ZoomManager.GetZoomFactor()))) // ELSE if!
                    {
                        ZoomManager.SetZoomFactorFixedLarge();
                    }
                }
                else if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F6))
                {
                    ZoomManager.SetZoomFactor(ZoomManager.GetZoomFactorCustomLast());
                }

                ScrollAfterZoomFactorChange(GUIElements, MouseEventProcessor, MousePosUnZoomedBeforeZoomFactorChange);

                GUIElements.GetMainWindow().DoRedraw();
            }
        }
    }

    @Override
    public void TryProcessUserMouseAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor, ObjectStorage ObjectStorage, IOProgram IOProgram)
    {
        // ********************************************************************
        // DO BEFORE CHECKING IF MOUSE EVENTS TO BE PROCESSED!
        // restore BackUp or end restoring mode
        // ->
        if (MouseEventProcessor.IsLeftButtonClicked() &&
            GUIElements.GetMainWindow().IsBackUpRestoreEnabled())
        {
            int BackUpFileIndex = (MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow()).y - 100) / 50; // must match font size used for drawing backup file list

            if (MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow()).y < 100 ||
                BackUpFileIndex < 0)
            {
                GUIElements.GetMainWindow().DisableBackUpRestore();
                GUIElements.GetMainWindow().DoRedraw();
            }
            else if (BackUpFileIndex >= 0 && BackUpFileIndex < GUIElements.GetMainWindow().GetBackUpRestoreFiles().size())
            {
                String LoadPath = GUIElements.GetMainWindow().GetBackUpRestoreFiles().get(BackUpFileIndex);

                if (LoadPath.length() > 0)
                {
                    if (Tools.ShowYesNoDialog(GUIElements.GetMainWindow(), "Restore \"" + LoadPath + "\"?") == true)
                    {
                        StopSimulation(GUIElements); // doesn't continue right if loading a new file, don't know exactly why, but continuing simulation is 1) confusing for the user and 2) doesn't really make sense

                        GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Restoring Backup", false, true, true, true);

                        LoadSaveDataEx LoadSaveDataEx = LoadFile(LoadPath, LoadSaveManager, true);

                        if (LoadSaveDataEx.GetSuccessOrError())
                        {
                            // LoadSaveManager.ReactOnFileLoaded(LoadPath, false);

                            GUIElements.GetIOPanel().SetIOProgramString(LoadSaveDataEx.GetIOProgramString());
                            IOProgram.SetIOProgramString(LoadSaveDataEx.GetIOProgramString());

                            GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("BACKUP RESTORED", true, false, false, false);

                            ZoomManager.SetZoomFactor(LoadSaveDataEx.GetZoomFactor());
                            ShowZoomFactorChangeMessage(GUIElements);
                        }
                        else
                        {
                            // LoadSaveManager.ReactOnFileLoadError();

                            Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error restoring Backup: " + LoadSaveDataEx.GetErrorMessage());
                        }

                        GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Restoring Backup");

                        GUIElements.GetMainWindow().DisableBackUpRestore();
                    }
                }

                GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
            }
        }
        // ********************************************************************

        if (!(MouseEventsToBeProcessed(GUIElements)))
            return;

        // ********************************************************************
        if (MouseEventProcessor.WasMouseWheelTurned())
        {
            Point MousePosUnZoomedBeforeZoomFactorChange = DrawObject.AlignOnGrid(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow())));

            if (MouseEventProcessor.WasMouseWheelTurnedUp())
            {
                double ZoomStep = (ZoomManager.GetZoomFactor() >= 0.10 ? 0.05 : 0.01);

                ZoomManager.SetZoomFactor(ZoomManager.GetZoomFactor() - ZoomStep);
            }
            if (MouseEventProcessor.WasMouseWheelTurnedDown())
            {
                double ZoomStep = (ZoomManager.GetZoomFactor() >= 0.05 ? 0.05 : 0.01);

                ZoomManager.SetZoomFactor(ZoomManager.GetZoomFactor() + ZoomStep);
            }

            ScrollAfterZoomFactorChange(GUIElements, MouseEventProcessor, MousePosUnZoomedBeforeZoomFactorChange);

            GUIElements.GetMainWindow().DoRedraw();
        }

        return;
    }

    private void VerifyEmptyFileExists(String WorkingDirectory)
    {
        String EmptyNCIDEFilePath = (WorkingDirectory + "/" + "empty.ncide").replaceAll("//", "/");

        if ((new File(EmptyNCIDEFilePath)).exists() == false)
        {
            ArrayList<DrawObject> EmptyDrawObjects = new ArrayList<DrawObject>();

            EmptyDrawObjects.clear(); // just to make sure there's nothing in it

            ObjectStorage.SaveEmptyFile(EmptyNCIDEFilePath);
        }
    }
}
